package net.lovesrc.allin.biz.dida;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.zhouzhou.fastjava.BizException;
import net.lovesrc.allin.biz.BaseService;
import net.lovesrc.allin.utils.CoodinateCovertor;
import net.lovesrc.allin.utils.LatLng;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class RideService extends BaseService {

    @Autowired
    private ListingService listingService;

    public void save(Ride ride) {
        logger.info("save, " + JSON.toJSONString(ride));
        if (ride.getSOriginLat() == null) {
            throw BizException.messageOf("出发地不能为空");
        }
        if (ride.getEOriginLat() == null) {
            throw BizException.messageOf("目的地不能为空");
        }
        LatLng sLatLng = CoodinateCovertor.bd_encrypt(LatLng.builder().lng(ride.getSOriginLng()).lat(ride.getSOriginLat()).build());
        ride.setSLat(sLatLng.getLat());
        ride.setSLng(sLatLng.getLng());

        LatLng eLatLng = CoodinateCovertor.bd_encrypt(LatLng.builder().lng(ride.getEOriginLng()).lat(ride.getEOriginLat()).build());
        ride.setELat(eLatLng.getLat());
        ride.setELng(eLatLng.getLng());

        if (ride.getId() == null || ride.getId() <= 0) {
            ride.setEnabled(true);
            ride.setNextRunTime(System.currentTimeMillis());
            ride.setEnabled(true);
            insertRide(ride);
        } else {
            Ride pRide = this.getById(ride.getId());
            pRide.setSOriginLat(ride.getSOriginLat());
            pRide.setSOriginLng(ride.getSOriginLng());
            pRide.setSLat(ride.getSLat());
            pRide.setSLng(ride.getSLng());
            pRide.setSName(ride.getSName());
            pRide.setSAddress(ride.getSAddress());
            pRide.setEOriginLat(ride.getEOriginLat());
            pRide.setEOriginLng(ride.getEOriginLng());
            pRide.setELat(ride.getELat());
            pRide.setELng(ride.getELng());
            pRide.setEName(ride.getEName());
            pRide.setEAddress(ride.getEAddress());
            pRide.setSTime1(ride.getSTime1());
            pRide.setSTime2(ride.getSTime2());
            pRide.setSDistance(ride.getSDistance());
            pRide.setEDistance(ride.getEDistance());
            pRide.setName(ride.getName());
            updateRide(pRide);
        }
    }

    private void updateRide(Ride ride) {
        Long id = ride.getId();
        JSONObject sets = toJsonObject(ride);
        sets.remove("targetSTime1");
        sets.remove("targetSTime2");
        sets.remove("id");
        updateRow(jdbcTemplate, "t_dida_ride", sets, "id = ?", id);
    }

    private void insertRide(Ride ride) {
        JSONObject row = toJsonObject(ride);
        row.remove("targetSTime1");
        row.remove("targetSTime2");
        row.remove("id");
        insertRow(jdbcTemplate, "t_dida_ride", row);
    }

    public List<Ride> getAll() {
        List<JSONObject> rows = queryForRows(jdbcTemplate, "select * from t_dida_ride order by enabled desc, updateTime desc");
        return rows.stream()
                .map(r -> r.toJavaObject(Ride.class))
                .collect(Collectors.toList());
    }

    public void changeEnableStatus(Long id, boolean enabled) {
        jdbcTemplate.update("update t_dida_ride set enabled = ? where id = ?", enabled, id);
    }

    public void nextRun(Long id, long delaySecondsAfterNow) {
        jdbcTemplate.update("update t_dida_ride set nextRunTime = ? where id = ?", System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(delaySecondsAfterNow), id);
    }

    public Ride getById(Long id) {
        return queryForRow(jdbcTemplate, "select * from t_dida_ride where id = ?", id).toJavaObject(Ride.class);
    }

    public Set<ListingHolder> query(Long id) {
        Ride ride = this.getById(id);
        List<Listing> listings = this.getBookingRideListV2(ride);
        return RideHelper.match(ride, listings);
    }

    public List<Listing> getBookingRideListV2(Ride ride) {
        Double rideSLat = ride.getSLat();
        Double rideSLng = ride.getSLng();
        Double rideELat = ride.getELat();
        Double rideELng = ride.getELng();
        // time
        LocalTime time;
        LocalTime nowTime = LocalTime.now();
        LocalTime rideSTime1 = ride.getTargetSTime1();
        LocalTime rideSTime2 = ride.getTargetSTime2();
        if (nowTime.isBefore(rideSTime1) || nowTime.isAfter(rideSTime2)) {
            time = rideSTime1;
        } else {
            int mode = nowTime.getMinute() % 10;
            if (mode == 0) {
                time = nowTime;
            } else {
                time = nowTime.plusMinutes(10 - mode);
            }
        }
        List<Listing> listings = listingService.getBookingRideListV2(time, rideSLat, rideSLng, rideELat, rideELng);
        save2Db(ride, listings);
        return listings;
    }

    private void save2Db(Ride ride, List<Listing> listings) {
        for (Listing listing : listings) {
            JSONObject jsonListing = listing.getOrigin();
            long id = jsonListing.getLong("id");
            if (jdbcTemplate.queryForObject("select count(id) from t_dida_listing where id = ?", int.class, id) == 0) {
                JSONObject newRow = new JSONObject()
                        .fluentPut("id", id)
                        .fluentPut("rideId", ride.getId())
                        .fluentPut("rideText", JSON.toJSONString(ride))
                        .fluentPut("rideSName", ride.getSName())
                        .fluentPut("rideEName", ride.getEName())
                        .fluentPut("asPassengerAverageScore", jsonListing.getBigDecimal("as_passenger_average_score"))
                        .fluentPut("createTime", jsonListing.getDate("create_time"))
                        .fluentPut("driverReceivedPrice", jsonListing.getBigDecimal("driver_received_price"))
                        .fluentPut("fromPoi", jsonListing.getJSONObject("from_poi").toJSONString())
                        .fluentPut("matchLevel", jsonListing.getIntValue("match_level"))
                        .fluentPut("matchPercent", jsonListing.getIntValue("match_percent"))
                        .fluentPut("passengerUserInfo", jsonListing.getJSONObject("passenger_user_info").toJSONString())
                        .fluentPut("passengerCid", jsonListing.getJSONObject("passenger_user_info").getString("cid"))
                        .fluentPut("personNum", jsonListing.getIntValue("person_num"))
                        .fluentPut("planStartTime", jsonListing.getDate("plan_start_time"))
                        .fluentPut("price", jsonListing.getBigDecimal("price"))
                        .fluentPut("priceText", jsonListing.getString("priceText"))
                        .fluentPut("relation", jsonListing.getIntValue("relation"))
                        .fluentPut("relationship", jsonListing.getJSONObject("relationship").toJSONString())
                        .fluentPut("reviveable", jsonListing.getIntValue("reviveable"))
                        .fluentPut("status", jsonListing.getString("status"))
                        .fluentPut("thanksPrice", jsonListing.getIntValue("thanks_price"))
                        .fluentPut("timeScaleMins", jsonListing.getIntValue("time_scale_mins"))
                        .fluentPut("timeType", jsonListing.getIntValue("time_type"))
                        .fluentPut("toPoi", jsonListing.getJSONObject("to_poi").toJSONString())
                        .fluentPut("type", jsonListing.getIntValue("type"))
                        .fluentPut("originText", jsonListing.toJSONString());
                insertRow(jdbcTemplate, "t_dida_listing", newRow);
            }
        }
    }

    public JSONObject getAndSaveRideOrderDetail(Long rideId) {
        JSONObject resJson = listingService.getRideOrderDetail(rideId);
        JSONObject rideDetail = resJson.getJSONObject("ride");
        insertRideDetail(rideDetail);
        return rideDetail;
    }

    private void insertRideDetail(JSONObject rideDetail) {

    }

    public void delete(String id) {
        jdbcTemplate.update("delete from t_dida_ride where id = ?", id);
    }

    public Set<ForecastListing> forecast(Long id) {

        Map<ListingHolder, List<String>> result = Maps.newHashMap();

        Ride ride = getById(id);
        LocalTime sTime1 = ride.getTargetSTime1();
        LocalTime sTime2 = ride.getTargetSTime2().plusMinutes(60);
        for (int i = 0; i < 7; i++) {
            Date now = new Date();
            Date nowBeforeDays = DateUtils.addDays(now, -i);
            String dateString = DateFormatUtils.format(nowBeforeDays, "yyyy-MM-dd");

            Calendar sTime1calendar = Calendar.getInstance();
            sTime1calendar.setTime(nowBeforeDays);
            sTime1calendar.set(Calendar.HOUR_OF_DAY, sTime1.getHour());
            sTime1calendar.set(Calendar.MINUTE, sTime1.getMinute());
            Date startTime1 = sTime1calendar.getTime();

            Calendar sTime2calendar = Calendar.getInstance();
            sTime2calendar.setTime(nowBeforeDays);
            sTime2calendar.set(Calendar.HOUR_OF_DAY, sTime2.getHour());
            sTime2calendar.set(Calendar.MINUTE, sTime2.getMinute());
            Date startTime2 = sTime2calendar.getTime();

            List<JSONObject> rows = queryForRows(jdbcTemplate, "select * from t_dida_listing" +
                    " where rideId = ?" +
                    " and planStartTime > ? and planStartTime < ?" +
                    " and matchPercent >= 70", id, startTime1, startTime2);
            List<Listing> listings = rows.stream()
                    .map(r -> r.getJSONObject("originText").toJavaObject(Listing.class))
                    .collect(Collectors.toList());
            Set<ListingHolder> listingHolders = RideHelper.match(ride, listings);
            for (ListingHolder listingHolder : listingHolders) {
                result.putIfAbsent(listingHolder, Lists.newArrayList());
                List<String> days = result.get(listingHolder);
                if (!days.contains(dateString)) {
                    days.add(dateString);
                }
            }
        }
        Set<ForecastListing> forecastListings = Sets.newTreeSet();
        for (Map.Entry<ListingHolder, List<String>> entry : result.entrySet()) {
            forecastListings.add(new ForecastListing(entry.getKey(), entry.getValue()));
        }
        return forecastListings;
    }

}